#include <functional>
#include "ohos_video_render_base.h"
#include "ohos_video_render_ohos.h"
#include "rtc_base/thread.h"
#include "rtc_base/logging.h"
#include "api/video/video_render_manager.h"
#include "common_video/ohos/ohos_egl_context_manager.h"

namespace webrtc {

constexpr const char* VIDEO_RENDER_TAG = "VideoRenderOhos";
constexpr const char* VIDEO_RENDER_ANIMATION_TASK = "VideoRenderAnimationTask";
constexpr const uint32_t VIDEO_RENDER_ANIMATION_TASK_ID = 253;
constexpr const int RENDER_ANIMATION_DELAY = 16;

/******** xComponent **********/
void OnSurfaceCreatedCB(OH_NativeXComponent* component, void* window) {
  if ((component == nullptr) || (window == nullptr)) {
    return;
  }

  std::string id = VideoRenderOhos::GetXComponentId(component);
  RTC_LOG(LS_INFO) << "OnSurfaceCreatedCB: " << id << ", window: " << window;

  if (id.empty()) {
    return;
  }

  uint64_t width;
  uint64_t height;
  int32_t xSize = OH_NativeXComponent_GetXComponentSize(component, window, &width, &height);

  if (xSize == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
    auto managerPtr = VideoRenderManager::GetInstance();
    auto renderPtr = managerPtr->GetRender(id);
    if (renderPtr) {
      renderPtr->Init(window,
                      nullptr,
                      static_cast<int>(width),
                      static_cast<int>(height));
    }
  }
}

void OnSurfaceChangedCB(OH_NativeXComponent* component, void* window) {
  if ((component == nullptr) || (window == nullptr)) {
    return;
  }

  std::string id = VideoRenderOhos::GetXComponentId(component);

  if (id.empty()) {
    return;
  }

  uint64_t width;
  uint64_t height;
  OH_NativeXComponent_GetXComponentSize(component, window, &width, &height);

  RTC_LOG(LS_INFO) << "OnSurfaceChangedCB: " << id << ", window: " << window
                   << ", width: " << width << " height: " << height;

  auto managerPtr = VideoRenderManager::GetInstance();
  auto renderPtr = managerPtr->GetRender(id);
  if (renderPtr) {
    renderPtr->SizeChange(static_cast<int>(width), static_cast<int>(height));
  }
}

void OnSurfaceDestroyedCB(OH_NativeXComponent* component, void* window) {
  if ((component == nullptr) || (window == nullptr)) {
    return;
  }

  std::string id = VideoRenderOhos::GetXComponentId(component);
  RTC_LOG(LS_INFO) << "OnSurfaceDestroyedCB: " << id << ", window: " << window;

  if (id.empty()) {
    return;
  }

  auto managerPtr = VideoRenderManager::GetInstance();
  auto renderPtr = managerPtr->GetRender(id);
  if (renderPtr) {
    renderPtr->Release();
    managerPtr->DisposeRender(id);
  }
}

VideoRenderOhos::VideoRenderOhos()
      : render_id_(),
      render_(std::make_shared<BaseVideoRender>()),
      render_thread_(nullptr),
      render_callback_(std::make_unique<OH_NativeXComponent_Callback>()),
      is_render_working_(false) {
}

void VideoRenderOhos::StartRenderThread() {
  if (!is_render_working_.exchange(true, std::memory_order_acquire)) {
    RTC_LOG(LS_INFO) << VIDEO_RENDER_TAG << " StartRenderThread";
    render_thread_ = rtc::Thread::Create();
    render_thread_->SetName(VIDEO_RENDER_TAG, nullptr);
    render_thread_->Start();
  }
}

void VideoRenderOhos::StopRenderThread() {
  if (is_render_working_.exchange(false, std::memory_order_release)) {
    RTC_LOG(LS_INFO) << VIDEO_RENDER_TAG << " StopRenderThread";
    render_thread_->Stop();
  }
}

void VideoRenderOhos::RunOnRenderThread(const std::function<void()> &function) {
  if (!is_render_working_.load()) {
    return;
  }
  render_thread_->PostTask([task = std::move(function)] {    
    task();
  });
}

bool VideoRenderOhos::Init(void *window, void *share_context, int width, int height) {
  StartRenderThread();
  RunOnRenderThread([render_this{render_}, window, share_context, width, height]() {
    render_this->Init(window, share_context, width, height);
  });
  return true;
}

void VideoRenderOhos::SetMirror(bool mirror) {
  render_->SetMirror(mirror);
}

bool VideoRenderOhos::GetMirror() const {
  return render_->GetMirror();
}

void VideoRenderOhos::SetScaleType(webrtc::VideoScaleMode h_match_scale_type,
                                      webrtc::VideoScaleMode h_mismatch_scale_type,
                                      webrtc::VideoScaleMode v_match_scale_type,
                                      webrtc::VideoScaleMode v_mismatch_scale_type) {
  if (!is_render_working_.load()) {
    return;
  }
  RunOnRenderThread([render_this{render_}, h_match_scale_type, h_mismatch_scale_type, v_match_scale_type, v_mismatch_scale_type]() {
    render_this->SetScaleType(h_match_scale_type, h_mismatch_scale_type, v_match_scale_type, v_mismatch_scale_type);
  });
}

void VideoRenderOhos::SizeChange(int width, int height) {
  if (!is_render_working_.load()) {
    return;
  }
  RunOnRenderThread([render_this{render_}, width, height]() {
    render_this->SizeChange(width, height);;
  });
}

void VideoRenderOhos::ClearColor(const std::vector<float>& rgba_color) {
  if (!is_render_working_.load()) {
    return;
  }
  RunOnRenderThread([render_this{render_}, rgba_color]() {
    render_this->ClearColor(rgba_color);
  });
}

void VideoRenderOhos::Release() {
  if (!is_render_working_.load()) {
    return;
  }
  RunOnRenderThread([render_this{render_}]() {
    render_this->Release();
  });
  StopRenderThread();
}

void VideoRenderOhos::OnFrame(const webrtc::VideoFrame& frame) {
  RunOnRenderThread([render_this{render_}, frame]() mutable {
    render_this->OnFrame(frame);
  });
}

std::string VideoRenderOhos::GetRenderId() const {
  return render_id_;
}

std::string VideoRenderOhos::GetXComponentId(OH_NativeXComponent *component) {
  char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'};
  uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
  if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
      return nullptr;
  }

  std::string id(idStr);
  return id;
}

void VideoRenderOhos::AttachHandle(void* handle) {
  OH_NativeXComponent* nativeXComponent = static_cast<OH_NativeXComponent*>(handle);
  render_id_ = GetXComponentId(nativeXComponent);
  RTC_LOG(LS_INFO) << "AttachHandle. XComponent: " << nativeXComponent << " " << render_id_;

  render_callback_->OnSurfaceCreated = OnSurfaceCreatedCB;
  render_callback_->OnSurfaceChanged = OnSurfaceChangedCB;
  render_callback_->OnSurfaceDestroyed = OnSurfaceDestroyedCB;
  OH_NativeXComponent_RegisterCallback(nativeXComponent, render_callback_.get());
}

void VideoRenderOhos::AddRenderListener(std::shared_ptr<VideoRenderListener> listener) {
  RunOnRenderThread([render_this{render_}, listener]() {
    render_this->AddRenderListener(listener);
  });
}

void VideoRenderOhos::RemoveRenderListener(std::shared_ptr<VideoRenderListener> listener) {
  RunOnRenderThread([render_this{render_}, listener]() {
    render_this->RemoveRenderListener(listener);
  });
}
}